package spring.fox.scanner;


import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.env.Environment;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
import spring.fox.annotation.RpcService;
import spring.fox.bean.RpcServiceBean;
import spring.fox.config.RpcPropertyValues;

import java.lang.annotation.Annotation;
import java.util.Arrays;
import java.util.Set;

/**
 * @author cuichao
 * @Description: RpcServiceAnnotationClassPathDefinitionScanner
 * @Date: create in 2021/3/4 20:49
 */
public class RpcServiceAnnotationClassPathDefinitionScanner extends ClassPathBeanDefinitionScanner {
    private Environment environment;

    private final ClassLoader classLoader;

    private final String[] ignoreAttributeNames;

    private final Class<? extends Annotation> annotationType = RpcService.class;

    public RpcServiceAnnotationClassPathDefinitionScanner(BeanDefinitionRegistry registry, ClassLoader classLoader, Environment environment) {
        super(registry, false);
        this.classLoader = classLoader;
        this.environment = environment;
        this.ignoreAttributeNames = new String[]{"interfaceName", "interfaceClass"};
        super.addIncludeFilter(new AnnotationTypeFilter(annotationType));
    }


    @Override
    public Set<BeanDefinitionHolder> doScan(String... basePackages) {

        Set<BeanDefinitionHolder> holders = super.doScan(basePackages);
        if (holders.isEmpty()) {
            logger.warn("No Fox Rpc-Service was found in '" + Arrays.toString(basePackages) + "' package. Please check your configuration.");
        } else {
            //生成SpringBeanDefinition后创建RpcDefinition
            try {
                registerRemoteRpcServiceBeanDefinition(holders, getRegistry());
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }
        return holders;
    }

    /**
     * 注册Rpc-Service-BeanDefinition
     *
     * @param beanDefinitions
     * @param registry
     * @throws ClassNotFoundException
     */
    public void registerRemoteRpcServiceBeanDefinition(Set<BeanDefinitionHolder> beanDefinitions, BeanDefinitionRegistry registry) throws ClassNotFoundException {

        for (BeanDefinitionHolder holder : beanDefinitions) {
            String beanName = holder.getBeanName();
            BeanDefinition beanDefinition = holder.getBeanDefinition();
            Class<?> beanClass = ClassUtils.forName(beanDefinition.getBeanClassName(), classLoader);
            //得到RpcService
            RpcService service = (RpcService) AnnotationUtils.findAnnotation(beanClass, annotationType);
            //解析Rpc-Service 所要注册的接口
            Class<?> serviceInterface = resolveServiceInterface(beanClass, service);
            //build Rpc-Service-BeanDefinition
            BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(RpcServiceBean.class);
            builder.addConstructorArgValue(service);
            builder.addPropertyValue(RpcServiceBean.RPC_SERVICE_SOURCE_BEAN_NAME, beanName);
            builder.addPropertyReference(RpcServiceBean.RPC_SERVICE_SOURCE_BEAN_REF, beanName);
            builder.addPropertyValue(RpcServiceBean.RPC_SERVICE_REGISTER_INTERFACE, serviceInterface);
            AbstractBeanDefinition serviceBeanDefinition = builder.getBeanDefinition();
            //RpcBean 属性赋值
            serviceBeanDefinition.getPropertyValues().addPropertyValues(new RpcPropertyValues(service, environment, true, ignoreAttributeNames));
            //生成 Rpc-Service-BeanName
            String serviceBeanName = generateServiceBeanName(service, serviceInterface, beanName);
            //register
            registerBeanDefinition(new BeanDefinitionHolder(serviceBeanDefinition, serviceBeanName), registry);
        }
    }


    /**
     * 得到Rpc-Service 需要注册的接口
     *
     * @param serviceClass
     * @return
     */
    private Class<?> resolveServiceInterface(Class<?> serviceClass, RpcService service) {
        Class<?> interfaceClass = service.interfaceClass();

        if (void.class.equals(interfaceClass)) {
            interfaceClass = null;

            String interfaceClassName = service.interfaceName();

            if (StringUtils.hasText(interfaceClassName)) {
                if (ClassUtils.isPresent(interfaceClassName, classLoader)) {
                    interfaceClass = ClassUtils.resolveClassName(interfaceClassName, classLoader);
                }
            }
        }

        //查找Service类继承的接口
        if (interfaceClass == null) {
            Class<?>[] interfaces = ClassUtils.getAllInterfacesForClass(serviceClass);
            if (interfaces.length > 0) {
                interfaceClass = interfaces[0];
            }
        }
        //valid
        Assert.notNull(interfaceClass,
                "Rpc-Service: " + serviceClass.getName() +
                        " must be implements interface or Annotation interfaceClass in not null");

        return interfaceClass;
    }

    /**
     * 生成Rpc-ServiceBeanDefinitionName
     *
     * @param service
     * @param rpcInterface
     * @param resourceName
     * @return
     */
    private String generateServiceBeanName(RpcService service, Class<?> rpcInterface, String resourceName) {
        return rpcInterface.getSimpleName() + "_RpcService";
    }


}
